# coding: utf-8


import pandas as pd
import requests
import json
import time
from requests.exceptions import ConnectTimeout, SSLError, ReadTimeout, ConnectionError
from retrying import retry
from urllib.parse import urljoin
from quant_researcher.quant.project_tool.time_tool import (datetime_to_timestamp, timestamp_to_str)
from quant_researcher.quant.project_tool.crypto import QA_util_find_missing_kline
from quant_researcher.quant.project_tool.logger.my_logger import LOG
from quant_researcher.quant.datasource_fetch.crypto_api.huobi.huobi_realtime import (
    QA_Fetch_Huobi,
    format_huobi_data_fields,
    Huobi2QA_FREQUENCY_DICT,
    FREQUENCY_SHIFTING,
)

"""
火币api
具体api文档参考: 
https://huobiapi.github.io/docs/spot/v1/cn/
https://huobiapi.github.io/docs/dm/v1/cn
https://huobiapi.github.io/docs/coin_margined_swap/v1/cn/
https://huobiapi.github.io/docs/usdt_swap/v1/cn/
"""

TIMEOUT = 10
ILOVECHINA = "同学！！你知道什么叫做科学上网么？ 如果你不知道的话，那么就加油吧！蓝灯，喵帕斯，VPS，阴阳师，v2ray，随便什么来一个！我翻墙我骄傲！"
Huobi_base_url = 'https://api.huobi.pro'
Huobi_futures_base_url = 'https://api.hbdm.com'

FIRST_PRIORITY = [
    'atomusdt',
    'algousdt',
    'adausdt',
    'bchusdt',
    'bsvusdt',
    'btcusdt',
    'btchusd',
    'dashusdt',
    'dashhusd',
    'eoshusd',
    'eosusdt',
    'etcusdt',
    'etchusd',
    'ethhusd',
    'ethusdt',
    'hthusd',
    'htusdt',
    'hb10usdt',
    'ltcusdt',
    'trxusdt',
    'xmrusdt',
    'xrpusdt',
    'zecusdt'
]

huobi_base_url = "https://api.huobi.pro"


@retry(stop_max_attempt_number=3, wait_random_min=50, wait_random_max=100)
def QA_fetch_huobi_symbols():
    """
    Get Symbol and currencies
    """
    url = urljoin(Huobi_base_url, "/v1/common/symbols")
    retries = 1
    datas = list()
    while (retries != 0):
        try:
            req = requests.get(url, timeout=TIMEOUT)
            retries = 0
        except (ConnectTimeout, ConnectionError, SSLError, ReadTimeout):
            retries = retries + 1
            if (retries % 6 == 0):
                print(ILOVECHINA)
            print("Retry get_exchange_info #{}".format(retries - 1))
            time.sleep(0.5)
    if (retries == 0):
        msg_dict = json.loads(req.content)
        if (('status' in msg_dict) and (msg_dict['status'] == 'ok')
                and ('data' in msg_dict)):
            if len(msg_dict["data"]) == 0:
                return []
            for symbol in msg_dict["data"]:
                # 只导入上架交易对
                if (symbol['state'] == 'online'):
                    datas.append(symbol)

    return datas


@retry(stop_max_attempt_number=3, wait_random_min=50, wait_random_max=100)
def fetch_huobi_data_with_auto_retry(url, params):
    """
    Get the symbol‘s data
    """

    retries = 1
    while (retries != 0) and (retries <= 10):
        try:
            req = requests.get(url=url, params=params, timeout=TIMEOUT)
            # 防止频率过快被断连
            time.sleep(0.5)
            retries = 0
        except (ConnectTimeout, ConnectionError, SSLError, ReadTimeout):
            retries = retries + 1
            if (retries % 5 == 0):
                print(ILOVECHINA)
            print(f"Retry {url} #{retries - 1}")
            time.sleep(0.5)

        if (retries == 0):
            # 成功获取才处理数据，否则继续尝试连接
            datas = json.loads(req.content)
            if len(datas) == 0:
                print('该段区间数据读取为空')
                return None
            return datas

    return None


@retry(stop_max_attempt_number=3, wait_random_min=50, wait_random_max=100)
def QA_fetch_huobi_kline(
    symbol,
    start_time,
    end_time,
    frequency,
    callback_func
):
    """
    Get the latest symbol‘s candlestick data  
    当前 REST API 不支持自定义时间区间，如需要历史固定时间范围的数据，请参考 Websocket API 中的 K 线接口。
    """
    datas = list()
    retries = 1
    url = urljoin(
        Huobi_base_url,
        "/market/history/kline?symbol={:s}&period={:s}&siz={:d}".format(
            symbol,
            frequency,
            1980
        )
    )
    while (retries != 0):
        try:
            req = requests.get(url, timeout=TIMEOUT)
            # 防止频率过快被断连
            time.sleep(0.5)
            retries = 0
        except (ConnectTimeout, ConnectionError, SSLError, ReadTimeout):
            retries = retries + 1
            if (retries % 6 == 0):
                print(ILOVECHINA)
            print("Retry get_candlestick #{}".format(retries - 1))
            time.sleep(0.5)
        except HuobiApiException as e:
            print(e.error_code)
            print(e.error_message)
            print("Skipping '{}'".format(symbol))
            time.sleep(0.5)
            break

    if (retries == 0):
        # 成功获取才处理数据，否则继续尝试连接
        msg_dict = json.loads(req.content)
        if (('status' in msg_dict) and (msg_dict['status'] == 'ok')
                and ('data' in msg_dict)):
            if len(msg_dict["data"]) == 0:
                return None
            for kline in msg_dict["data"]:
                datas.append(kline)
            # 狗日huobi.pro的REST API kline时间戳排序居然是倒序向前获取，必须从后向前获取，而且有数量限制，
            # Request < 2000,
            if (callback_func is not None):
                frame = format_huobi_data_fields(datas, symbol, frequency)
                callback_func(frame, freq=Huobi2QA_FREQUENCY_DICT[frequency])

    if len(datas) == 0:
        return None

    # 归一化数据字段，转换填充必须字段，删除多余字段
    frame = format_huobi_data_fields(datas, symbol, frequency)
    return frame


@retry(stop_max_attempt_number=3, wait_random_min=50, wait_random_max=100)
def QA_fetch_huobi_kline_subscription(
    symbol,
    start_time,
    end_time,
    frequency,
    callback_func
):
    """
    Get the symbol‘s candlestick data by subscription
    """
    reqParams = {}
    reqParams['from'] = int(end_time - FREQUENCY_SHIFTING[frequency])
    reqParams['to'] = int(end_time)

    requested_counter = 1
    sub_client = QA_Fetch_Huobi(
        callback_save_data_func=callback_func,
        find_missing_kline_func=QA_util_find_missing_kline
    )
    datas = list()
    retries = 1
    while (reqParams['to'] > start_time):
        if ((reqParams['from'] > datetime_to_timestamp())) or \
            ((reqParams['from'] > reqParams['to'])):
            # 出现“未来”时间，一般是默认时区设置，或者时间窗口滚动前移错误造成的
            LOG.info(
                'A unexpected \'Future\' timestamp got, Please check self.missing_data_list_func param \'tzlocalize\' set. More info: {:s}@{:s} at {:s} but current time is {}'
                .format(
                    symbol,
                    frequency,
                    timestamp_to_str(reqParams['from']),
                    timestamp_to_str(
                        datetime_to_timestamp()
                    )
                )
            )
            # 跳到下一个时间段
            reqParams['to'] = int(reqParams['from'] - 1)
            reqParams['from'] = int(reqParams['from'] - FREQUENCY_SHIFTING[frequency])
            continue

        retries = 1
        frame = None
        while (retries != 0):
            try:
                frame = sub_client.run_request_historical_kline(
                    symbol,
                    frequency,
                    reqParams['from'],
                    reqParams['to'],
                    requested_counter
                )
                if (frame is None):
                    # 火币网的 WebSocket 接口机制很奇特，返回len(data)==0
                    # 就说明已经超越这个交易对的上架时间，不再有更多数据了。
                    # 当前 Symbol Klines 抓取已经结束了
                    return None
                else:
                    retries = 0
            except Exception:
                retries = retries + 1
                if (retries % 6 == 0):
                    print(ILOVECHINA)
                print("Retry request_historical_kline #{}".format(retries - 1))
                time.sleep(0.5)
            if (retries == 0):
                # 等待3秒，请求下一个时间段的批量K线数据
                reqParams['to'] = int(reqParams['from'] - 1)
                reqParams['from'
                         ] = int(reqParams['from'] - FREQUENCY_SHIFTING[frequency])
                requested_counter = requested_counter + 1
                # 这一步冗余，如果是开启实时抓取会自动被 WebSocket.on_messgae_callback 事件处理函数保存，
                # 但不确定不开启实时行情抓取会不会绑定on_messgae事件，所以保留冗余。
                callback_func(data=frame, freq=frequency)
        time.sleep(0.5)
    
    if ((retries == 0) and (len(frame) > 0)):
        return frame
    else:
        return None


def __request(url):
    # print(url)
    try:
        session = requests.Session()
        response = session.get(url, timeout=120)
    except requests.exceptions.RequestException:
        raise

    try:
        response.raise_for_status()
        content = json.loads(response.content.decode('utf-8'))
        return content
    except Exception as e:
        # check if json (with error message) is returned
        try:
            content = json.loads(response.content.decode('utf-8'))
            raise ValueError(content)
        # if no json
        except json.decoder.JSONDecodeError:
            pass

        raise


def __api_url_params(api_url, params, api_url_has_params=False):
    if params:
        # if api_url contains already params and there is already a '?' avoid
        # adding second '?' (api_url += '&' if '?' in api_url else '?'); causes
        # issues with request parametes (usually for endpoints with required
        # arguments passed as parameters)
        api_url += '&' if api_url_has_params else '?'
        for key, value in params.items():
            if type(value) == bool:
                value = str(value).lower()

            api_url += "{0}={1}&".format(key, value)
        api_url = api_url[:-1]
    return api_url


def fetch_huobi_funding_rate(symbol, market_type):
    """
    Get the latest symbol‘s funding_rate data
    时间倒序切片获取算法，是各大交易所获取1min数据的神器，因为大部分交易所直接请求跨月跨年的1min分钟数据
    会直接返回空值，只有将 start_epoch，end_epoch 切片细分到 200/300 bar 以内，才能正确返回 kline，
    火币和binance，OKEx 均为如此，直接用跨年时间去直接请求上万bar 的 kline 数据永远只返回最近200条数据。
    """

    if market_type == 'um_futures':
        url = urljoin(Huobi_futures_base_url, "/linear-swap-api/v1/swap_historical_funding_rate")
    elif market_type == 'cm_futures':
        url = urljoin(Huobi_futures_base_url, "/swap-api/v1/swap_historical_funding_rate")

    res_list = []
    page_num = 1
    Flag = True
    while Flag and (page_num < 100000):
        params = {
            "contract_code": symbol,
            "page_index": page_num,
            "page_size": 50
        }
        res = requests.get(url=url, params=params, timeout=500)
        res = pd.DataFrame(json.loads(res.text)['data']['data'])
        if res.empty:
            Flag = False  # 数据获取完毕
        else:
            res_list.append(res)
            page_num = page_num + 1
        time.sleep(1)
    all_df = pd.concat(res_list, ignore_index=True)

    if len(all_df) == 0:
        return None

    all_df.rename({'funding_time': 'timestamp'}, axis=1, inplace=True)
    all_df.sort_values(by='timestamp', inplace=True)

    return all_df


def fetch_huobi_margin_interest_rate():
    """
    Get the latest symbol‘s funding_rate data

    """
    header = {'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/105.0.0.0 Safari/537.36'}
    url = urljoin(Huobi_base_url, "/v1/cross-margin/loan-info")

    res = requests.get(url=url, headers=header, timeout=15)
    res = pd.DataFrame(json.loads(res.text)['data']['data'])

    # if res.empty:
    #     Flag = False  # 数据获取完毕
    # else:
    #     res_list.append(res)
    #     page_num = page_num + 1
    #
    # all_df.rename({'funding_time': 'timestamp'}, axis=1, inplace=True)
    # all_df.sort_values(by='timestamp', inplace=True)

    return


if __name__ == '__main__':
    # start = time.mktime(datetime.datetime(2015, 1, 1, 0, 0, 0, tzinfo=tzutc()).timetuple())
    # end = time.mktime(datetime.datetime(2022, 2, 9, 12, 0, 0, tzinfo=tzutc()).timetuple())

    # symbol = 'BTC-USDT'
    # # symbol = 'BTC-USD'
    # timezone = 'Asia/Shanghai'
    # # timezone = '+0000'
    #
    # if symbol == 'BTC-USDT':
    #     asset = 'BTC-USDT'
    #     market_type = 'um_futures'
    # elif symbol == 'BTC-USD':
    #     asset = 'BTC-USD'
    #     market_type = 'cm_futures'
    #
    # data = fetch_huobi_funding_rate(asset, market_type)
    #
    # data['timestamp'] = data.apply(lambda x: int(x['timestamp']) / 1000, axis=1)
    # data['date'] = pd.to_datetime(data['timestamp'], unit='s').dt.tz_localize('UTC').dt.tz_convert(timezone)
    # data['date'] = data['date'].dt.strftime('%Y-%m-%d')
    # data['datetime'] = pd.to_datetime(data['timestamp'], unit='s').dt.tz_localize('UTC').dt.tz_convert(timezone)
    # data['datetime'] = data['datetime'].dt.strftime('%Y-%m-%d %H:%M:%S')
    # data['time'] = pd.to_datetime(data['timestamp'], unit='s').dt.tz_localize('UTC').dt.tz_convert(timezone)
    # data['time'] = data['time'].dt.strftime('%H:%M:%S')
    # data.set_index('datetime', inplace=True)
    #
    # import os
    # from quant_researcher.quant.project_tool.localize import DATA_DIR
    # file_path = os.path.join(DATA_DIR, f'funding_rate')
    # os.makedirs(file_path, exist_ok=True)
    # file_name = os.path.join(file_path, f'huobi_{symbol}_funding_rate')
    # data.to_excel(f'{file_name}.xlsx')

    fetch_huobi_margin_interest_rate()